/**
 * 
 */
package com.google.code.netz.wfe.model;

import java.util.ArrayList;
import java.util.List;

/**
 * A Transition is synonymous with a ITask.
 * <p>
 * A Transition <code><b>'t'</b></code> is said to be enabled if and only if
 * each input {@link Place} of 't' contains at least one token.
 * <p>
 * A Transition can therefore be defined by the number of {@link Place}s it is
 * composed of.
 * <p>
 * An enabled {@link Transition} may fire. If a {@link Transition} fires, then
 * it consumes one {@link IToken} from each input {@link Place}, and produces
 * one IToken in each output {@link Place}.
 * <p>
 * A task is a Logical Unit ofWork (LUW), and an activity is a transaction. Just
 * like a transaction, an activity should satisfy the well-known ACID
 * properties.
 * <ul>
 * <li><i><b>A</b>tomicity</i>: An activity is executed successfully (commit) or
 * is rolled back completely, i.e., a task cannot be completed partially.</li>
 * <li><i><b>C</b>onsistency</i>: The execution of an activity leads to a
 * consistent state.</li>
 * <li><i><b>I</b>solation</i>: The effect of the execution of an activity in
 * parallel with other activities is equal to the effect of the execution of one
 * activity in isolation.</i>
 * <li><i><b>D</b>urability</i>: The result of a committed activity cannot get
 * lost.</li>
 * </ul>
 * 
 * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
 * @since 1.0
 * @version $Id: Transition.java 18 2009-02-09 15:38:23Z rahul.thakur.xdev@gmail.com $
 */
public class Transition<D> extends AbstractPetriNetEntity<D> {

	/**
	 * {@link InputArc}s to this Transition.
	 */
	private List<InputArc<?>> inputs;

	/**
	 * {@link OutputArc}s from this Transition.
	 */
	private List<OutputArc<?>> outputs;

	/**
	 * ITask associated to this Transition that it executes when fired.
	 */
	private ITask task;

	/**
	 * If <code>true</code> indicates that the Transition is internal to the
	 * WorkflowEngine and will not have any input/output arcs.
	 */
	private boolean isInternal = false;

	/**
	 * Default guard.
	 */
	private Guard guard = new Guard() {
		public boolean evaluate() {
			// Determine if there is atleast one IToken on
			// each of the input Places for this Transition.
			synchronized (this) {
				if (getCurrentState() == State.STATE_RUNNING || getCurrentState() == State.STATE_COMPLETED)
					return false; // Transitions only fire if not running
				List<InputArc<?>> inputsToTransition = getInputs();
				int numInputPlaces = inputsToTransition.size();
				int tokensFound = 0;
				for (InputArc<?> i : inputsToTransition) {
					Place<?> p = i.getSource();
					// for tokens found on each place, increment
					// token counter by 1
					if (p.getTokens().size() > 0)
						tokensFound++;
				}
				if (numInputPlaces == tokensFound)
					return true;
				return false;
			}
		}
	};

	/**
	 * 
	 * @param label
	 */
	public Transition(String label) {
		super(label);
	}

	/**
	 * Creates a Transition with a given label and ITask instance.
	 * 
	 * @param label
	 * @param task
	 */
	public Transition(String label, ITask task) {
		super(label);
		this.task = task;
	}

	/**
	 * Creates a Transition with a given label and ITask instance.
	 * 
	 * @param label
	 * @param task
	 * @param isInternal
	 *            if <code>true</code>, then the created {@link Transition}
	 *            instance is internal to the engine.
	 */
	public Transition(String label, ITask task, boolean isInternal) {
		super(label);
		this.task = task;
		this.isInternal = isInternal;
	}

	/**
	 * 
	 * @param i
	 */
	public void addInput(InputArc<?> i) {
		if (null == inputs)
			this.inputs = new ArrayList<InputArc<?>>();
		this.inputs.add(i);
	}

	/**
	 * 
	 * @param o
	 */
	public void addOutput(OutputArc<?> o) {
		if (null == outputs)
			this.outputs = new ArrayList<OutputArc<?>>();
		this.outputs.add(o);
	}

	/**
	 * Removes the specified {@link InputArc} connection to this Transition.
	 * 
	 * @param i
	 */
	public void removeInput(InputArc<?> i) {
		if (null != inputs && this.inputs.contains(i))
			this.inputs.remove(i);
	}

	/**
	 * Removes the specified {@link OutputArc} connection from this Transition.
	 * 
	 * @param o
	 */
	public void removeInput(OutputArc<?> o) {
		if (null != outputs && this.outputs.contains(o))
			this.outputs.remove(o);
	}

	/**
	 * @return the inputs
	 */
	public List<InputArc<?>> getInputs() {
		return inputs;
	}

	/**
	 * @return the outputs
	 */
	public List<OutputArc<?>> getOutputs() {
		return outputs;
	}

	/**
	 * @return the isInternal
	 */
	public boolean isInternal() {
		return isInternal;
	}

	/**
	 * @param guard
	 *            the guard to set
	 */
	public void setGuard(Guard guard) {
		this.guard = guard;
	}

	/**
	 * Determine if there is atleast one IToken on each of the {@link Place}s
	 * that define this Transition.
	 * 
	 * @return
	 * @deprecated Use {@link Transition#}
	 */
	public boolean isEnabled() {
		return this.guard.evaluate();
	}

	/**
	 * Returns the task associated with this Transition.
	 * 
	 * @return the task
	 */
	public ITask getTask() {
		return task;
	}

	/**
	 * Represents this transition guard.
	 */
	public abstract class Guard {

		/**
		 * Guard function.
		 * 
		 * @return <code>true</code> if this guard evaluates to enabled
		 *         transition/arc; <code>false</code> otherwise.
		 */
		public abstract boolean evaluate();

	} // Guard

}
